/*
 *  YATUN  - Yet another tunneling application
 *
 *  Michal Demin - 2007
 *
 *  IO_UDP.cpp
 *  UDP komunikacny modul
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>

#include "IO_UDP.h"

IO_UDP::IO_UDP(struct sockaddr_in * tmpaddr) {
    const char * name_loc = "IO_UDP";
    
    memset(&Name, 0, NAME_LENGHT); // nastavim meno
    memcpy(&Name, name_loc, 6);
    
    memset(&localaddr, 0, sizeof(struct sockaddr_in)); // znulujem struktury adries
    memset(&remoteaddr, 0, sizeof(struct sockaddr_in));

    memcpy(&localaddr, tmpaddr, sizeof(struct sockaddr_in)); // skopirujem adresu
    
    Flag = PLUG_FLAG_IO;
    listen_on = 0;
}

IO_UDP::IO_UDP() {
    const char * name_loc = "IO_UDP";
    
    
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons(DEFAULT_PORT);
    localaddr.sin_addr.s_addr = INADDR_ANY;

    memset(&localaddr, 0, sizeof(struct sockaddr_in));
    memset(&remoteaddr, 0, sizeof(struct sockaddr_in));
    memset(&Name, 0, NAME_LENGHT);
    memcpy(&Name, name_loc, 6);
    Flag = PLUG_FLAG_IO;
    listen_on = 0;
}

IO_UDP::~IO_UDP() {

}

void IO_UDP::SetAddress(struct sockaddr_in * tmpaddr) {
	listen_on = 0;
    memcpy(&remoteaddr, tmpaddr, sizeof(struct sockaddr_in));
}

int IO_UDP::Open() {
    struct timeval timeout;
    int reuse = 1;
    int ret;
    
// vytvorim si socket        
    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    if (ret) {
	printf("unable to set address reuse \n");
	return -1;
    }
    
// bindnem socket na port
    ret = bind(fd, (struct sockaddr *)&localaddr, sizeof(struct sockaddr));
    if (ret) {
        printf("unable to bind socket  \n");
        return -1;
    }

    timeout.tv_sec = 0;
    timeout.tv_usec = 200;

// nastavim timeouty pre posielanie  --- suu vlastne potrebneeee ? ;) 
    ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
    if(ret) {
        printf("Unable to set SEND TIMEOUT on socket\n");
        return -1;
    }

// nastavim timeout pre prijmanie
    ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
    if(ret) {
        printf("Unable to set RECIEVE TIMEOUT on socket\n");
        return -1;
    }


    return 0;
}

int IO_UDP::Listen() {
    listen_on = 1;
    return 0;
}

int IO_UDP::Connect() {
    return 0;
}

int IO_UDP::Close() {
    close(fd);
    return 0;
}

int IO_UDP::Recieve(Packet * Pack) {
    int ret; 
    int remaddrlen = sizeof(struct sockaddr);
    
//    int len = Pack->Lenght + 2*sizeof(short);
//    fprintf(stderr,"sizeof %d\t", sizeof(Packet));

    ret = recvfrom(fd, Pack, sizeof(Packet), 0, (struct sockaddr *)&remoteaddr, (socklen_t *)&remaddrlen);
    YATUN_PACKET_2HOST(Pack);

//    fprintf(stderr,"ret %d\t\n", ret);
/*
    if(ret != Pack->Lenght + YATUN_OVERHEAD) {
	fprintf(stderr, "recieved wrong size !!! ");
    }
*/

    if(ret <= 0)
	return -1;
    
    return ret;
}

int IO_UDP::Send(Packet * Pack) {
    int ret;
    int len = Pack->Lenght + YATUN_OVERHEAD; // + overhead struktury packet

//    Pack->Lenght = htons(Pack->Lenght);      // koli LITTLE a BIG indianom
    YATUN_PACKET_2NET(Pack);
    ret = sendto(fd, Pack, len, 0, (struct sockaddr *)&remoteaddr, sizeof(struct sockaddr));
//    Pack->Lenght = ntohs(Pack->Lenght); 
    YATUN_PACKET_2HOST(Pack); // aby sme nezmenili moc packet
//    Pack->Lenght = ntohs(Pack->Lenght); 

    return ret;
}

int IO_UDP::DataReady() {
    struct timeval timeout;
    fd_set rfds;
    int ret;
    
    timeout.tv_sec = 0;
    timeout.tv_usec = 20;
    
    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);
    
    ret = select(fd+1, &rfds, NULL, NULL, &timeout);
    
    if (ret == -1) {
	return 0;
    } else if (ret) {
	//fprintf(stderr,"Data is available now.\n");
	if(FD_ISSET(fd, &rfds)) {
	    return 1;
	}
    }
    
    return 0;
}

int IO_UDP::GetFD() {
    return fd;
}
